/*
 * DefaultRegister.java   2008/07/28
 *
 * Copyright 2008 Web & Zen Multimedia, All rights reserved.
 * Use is subject to license terms.
 */
package it.wzm.lightweightlog;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Properties;

/**
 * Default implementation of Register, it simply extends Register and store passed Dumper
 * into an ArrayList, called slot.
 */
final class SynchronizedRegister<T extends Dumper> extends Register<T> implements Serializable {

	/**
     * As required by serialization
     */
	private static final long serialVersionUID = -1107606779436763082L;
    
    /**
     * Where all the Dumper are stored
     */
    private LinkedList<T> slot;
	/** 
	 * where all our Threads will be stored...
	 */
	private LinkedList<Thread> threads;

    /**
     * Create register from a given Array of Dumper, null array cause ObjectInitializationException.
     * If in the passed array some Dumper index is null.
     * @param t Array of dumper
     */
    public SynchronizedRegister(Properties p, ArrayList<T> t) {
        // doing our Duty...
        super(p,t);
    }

    @Override
    final void preinit() {
    	// this where our Dumper will be saved...
    	this.slot = new LinkedList<T>();
    }
    
    @Override final void postinit() {
    	// where all of our thread will be stored...
    	this.threads = new LinkedList<Thread>();  	
    }
    
    /**
     * Save a passed Dumper into the slot, if the slot already contains this Dumper,
     * then nothing is stored and false is returned.
     * @param t Anything extending Dumper
     * @return true if Dumper is properly added false if not
     */
    boolean save(T t) {
        return slot.add(t);
    }

    /**
     * Remove a passed Dumper from the slot, if the slot already does not contains this Dumper,
     * or the Dumper status is not FAILURE then nothing is removed and false is returned.
     * @param t Anything extending Dumper
     * @return true if Dumper is properly added false if not
     */
    boolean remove(T t) {
        // easy check...
        if (!slot.contains(t)) {
            return false;
        // only Dumper with status FAILURE can be removed
        }
        if (t.getStatus() == Dumper.Status.FAILURE) {
            return slot.remove(t);
        } else {
            return false;
        }
    }

    /**
     * The size of the slot
     * @return int
     */
    int size() {
        return slot.size();
    }

    /**
     * Dispatch entries to each Dumper in slot
     * @param ll A LinkedList of Entry
     */
    synchronized <E extends Entry> void dispatchToDumper(LinkedList<E> ll) {
   		
        // browse each Dumper
        dumping:
        for (T t: slot) {

            // get status...
            if (t.getStatus() == Dumper.Status.FAILURE && t.hasKillOnFailure()) {
            	this.remove(t);
                continue dumping;
            }

            // this class will act as a ThreadWrapper
            // for starting each dumper independently 
            // from the other...
            final class Wrapper implements Runnable {          	

            	// this will hold our Dumper
            	private Dumper pd;
            	// this will hold our list of Entry
            	private LinkedList<E> pll;
            	
            	// the only constructor
            	public Wrapper(Dumper d, LinkedList<E> ll) {
            		this.pd=d;
            		this.pll=ll;
            	}
           	
            	// implement run...
            	public void run() {
		            try {
		                // opening
		                this.pd.prepare();
		                // browse each Entry in list
		                // we know by contract that
		                // our passed list contains
		                // a sorted group of Entries	                
		                for (Entry e: pll)
		                	this.pd.dump(e);

		            } catch (DumperException ex) {
		                LogFactory.getLog().error(ex);
		            } finally {
		                try {
		                    // finally closing...
		                    this.pd.release();
		                } catch (DumperException ex) {
		                	LogFactory.getLog().error("Cannot close Dumper: " + ex.getMessage());
		                }
		            }
            	}
	        };
	        // we now implement and start each Wrapper...
	        Thread td = new Thread(new Wrapper(t,ll));
	        td.setName(t.getName());
	        td.setDaemon(false);
	        td.start();
	        // finally adding to list...
	        threads.add(td);
        }
        
        // iterate and perform join() action
        for (Thread it: threads) {
        	try {
				it.join();
			} catch (InterruptedException e) {
				LogFactory.getLog().error(e);
			}
        }
        // cleaning resources...
        threads.clear();
    }

    /**
     * Dispatch a single Entry to all Dumper in the slot
     * @param e Anything extending Entry
     */
    <E extends Entry> void dispatchToDumper(E e) {

        // browse each Dumper
        for (Dumper d: slot) {
            try {
                // the following is normal routine
                d.prepare();
                d.dump(e);
                d.release();
            } catch (DumperException ex) {
            	LogFactory.getLog().error(ex);
            }
        }
    }
     
    /**
     * Close this Register and all Dumper in it
     */
    @Override
    void shutdown() {
        // browse each Dumper
        for (T t: slot) {
            try {
                t.release(true);
                this.remove(t);
            } catch (DumperException ex) {
            	LogFactory.getLog().error(ex);
            }
        }
    }    
    
}
